﻿#region Copyright ©2008-2011, Technische Universitaet Muenchen

// ====================================================================================================
//
// Last Changed by       $Author: then $
// Last Changed Date     $LastChangedDate: 2009-12-16 16:37:27 +0100 (Wed, 16 Dec 2009) $
// Last Changed Revision $Rev: 504 $
//
// ====================================================================================================

#endregion

#region

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Linq;
using Tum.CollabXT.VModellXT.Resources;
using Tum.CollabXT.VModellXT.Tailoring;

#endregion

namespace Tum.CollabXT.VModellXT
{
    public class VModellXTProcessProvider : IProcessProvider
    {
        #region Private Members (data structure)

        private readonly Dictionary<string, object> _ExtendedData = new Dictionary<string, object>();

        /// <summary>
        /// Collection of all "Vorgensbaustein"-IDs of this project
        /// </summary>
        private ICollection<string> ProjectVbIdList;

        #endregion

        public static string GetProviderName()
        {
            return Language.ProviderInfo_Name;
        }

        public static string GetProviderDescription()
        {
            return Language.ProviderInfo_Description;
        }

        public static string GetProviderAuthor()
        {
            return "© 2011 TU München";
        }

        #region IProcessProvider Member: Init

        public void Initialize(Log log)
        {
            log.AddEntry(Language.Loading_PluginInfo);
            log.AddEntry(Language.Loading_Copyright);
            log.AddSeparator();

            if (VModellFile == null)
                throw new VMXTException(Language.Error_Inititalizing_NoVmxt);

            if (!UseInternalTailoring)
            {
                if (ProjectFile == null)
                    throw new VMXTException(Language.Error_Inititalizing_NoProjectFile);

                LoadUsingProjectFile(log);

                log.AddEntry(Language.Loading_CompletedSuccessfully);
            }
            else
            {
                //Save tailored model and project file
                string tailoredModelPath = SaveInternalTailoringModel ? SaveInternalTailoringModelPath : Path.Combine(Path.GetTempPath(), "VMXT-" + (new Random()).Next());
                string tailoredProjectPath = SaveInternalTailoringModel ? Path.Combine(SaveInternalTailoringModelPath, "project.vmp") : Path.GetTempFileName();

                Directory.CreateDirectory(tailoredModelPath);

                log.AddEntry(Language.Loading_TailoringModel);
                var finalTailoring = InternalTailoring.BuildTailoring();
                finalTailoring.SaveProjectVModell(tailoredModelPath);

                log.AddEntry(Language.Loading_ProjectFile);
                finalTailoring.SaveVmpProjectFile(tailoredProjectPath);


                //Execute provider logic with the tailored model and project file
                //Save original provider settings
                var originalVModellFile = VModellFile;
                var originalProjectFile = ProjectFile;
                try
                {
                    VModellFile = Path.Combine(tailoredModelPath, "V-Modell-XT.xml");
                    ProjectFile = tailoredProjectPath;
                    UseInternalTailoring = false;

                    Initialize(log);
                }
                finally
                {
                    //Restore original provider settings
                    VModellFile = originalVModellFile;
                    ProjectFile = originalProjectFile;
                    UseInternalTailoring = true;

                    //Remove temporary files
                    if (!SaveInternalTailoringModel)
                    {
                        Directory.Delete(tailoredModelPath, true);
                        File.Delete(tailoredProjectPath);
                    }
                }
            }
        }

        /// <summary>
        /// Initializes all structures with the data given in the VMP file.
        /// </summary>
        /// <param name="log">Log to be used.</param>
        private void LoadUsingProjectFile(Log log)
        {
            log.AddEntry(Language.Loading_Initializing);
            var modelDocument = new XmlDocument();
            modelDocument.Load(VModellFile);

            var vmpDocument = new XmlDocument();
            vmpDocument.Load(ProjectFile);

            //Load process name and description
            var nameNode = vmpDocument.SelectSingleNode(Queries.VMP_GetProjectName);
            Name = nameNode != null ? Helper.GetTextFromHtmlString(nameNode.InnerText) : "V-Modell XT";

            var descNode = modelDocument.SelectSingleNode(Queries.MDL_GetModelDescription);
            if (descNode != null)
                Description = Helper.GetTextFromHtmlString(descNode.InnerText);


            //Instantiate lists
            ProjectVbIdList = new HashSet<string>();
            Activities = new List<IActivity>();
            Tasks = new List<ITask>();
            Milestones = new List<IMilestone>();
            Roles = new List<IRole>();
            Disciplines = new List<IDiscipline>();
            Artifacts = new List<IArtifact>();
            Topics = new List<ITopic>();
            Associations = new List<IAssociation>();

            //Load elements
            LoadVBs(modelDocument, vmpDocument, log);

            log.AddEntry(Language.Loading_Products);
            LoadProducts(modelDocument, vmpDocument, log);

            log.AddEntry(Language.Loading_DecisionGates);
            LoadDecisionGates(modelDocument, vmpDocument, log);

            log.AddEntry(Language.Loading_Roles);
            LoadRoles(modelDocument, log);

            log.AddEntry(Language.Loading_Dependencies);
            LoadDependencies(modelDocument, log);
        }

        /// <summary>
        /// Loads "Vorgehensbausteine" from a VMP-file
        /// </summary>
        /// <param name="modelDocument">V-Model document.</param>
        /// /// <param name="vmpDocument">VMP document.</param>
        /// <param name="log">Log to be used.</param>
        private void LoadVBs(XmlNode modelDocument, XmlNode vmpDocument, Log log)
        {
            var vbNodes = vmpDocument.SelectNodes(Queries.VMP_GetVBs);
            foreach (XmlNode curVbNode in vbNodes)
                ProjectVbIdList.Add(curVbNode.Attributes["link"].Value);
        }


        /// <summary>
        /// Loads milestones from a VMP file.
        /// </summary>
        /// <param name="modelDocument">V-Model document.</param>
        /// <param name="vmpDocument">VMP document.</param>
        /// <param name="log">Log to be used.</param>
        private void LoadDecisionGates(XmlNode modelDocument, XmlNode vmpDocument, Log log)
        {
            var mainStatusNr = 0;
            var iterationNr = 0;
            var inIteration = false;

            var milestoneInformationNodes = vmpDocument.SelectNodes(Queries.VMP_GetMilestones);
            foreach (XmlNode curMilestoneNode in milestoneInformationNodes)
            {
                try
                {
                    var decisionGate = new DecisionGate();

                    //Get milestone data
                    if (curMilestoneNode.Attributes.GetNamedItem("id") != null)
                        decisionGate.Id = curMilestoneNode.Attributes["id"].Value; //Only set temporarily. Is updated later, if possible
                    else
                    {
                        log.AddEntry("Milestone without id found. Skipped.", LogEntryType.Warning);
                        continue;
                    }

                    var nameNode = curMilestoneNode.SelectSingleNode("Name");
                    if(nameNode != null)
                        decisionGate.Name = nameNode.InnerText;
                    else
                    {
                        log.AddEntry("Milestone '" + decisionGate.Id + "' has no name. Skipped.", LogEntryType.Warning);
                        continue;
                    }

                    var dateNode = curMilestoneNode.SelectSingleNode("Datum");
                    if (dateNode != null)
                        decisionGate.ScheduledDate = DateTime.Parse(dateNode.InnerText);

                    var nrNode = curMilestoneNode.SelectSingleNode("Nummer");
                    if (nrNode != null)
                        decisionGate.ScheduleNumber = Helper.ParseNumber(nrNode.InnerText);

                    #region Save predecessor ids

                    var predecessorList = new List<string>();
                    decisionGate.ExtendedData["__VMXT_PredecessorList"] = predecessorList;

                    var predecessorNodes = curMilestoneNode.SelectNodes("Vorgänger/MSRef");
                    foreach (XmlNode curPredecessor in predecessorNodes)
                    {
                        var predecessorLinkAttr = curPredecessor.Attributes["link"];
                        if(predecessorLinkAttr != null)
                            predecessorList.Add(predecessorLinkAttr.Value);
                    }

                    #endregion

                    //## Get type and description of this decision gate
                    //Get taskId of the "Ablaufentscheidungspunkt"
                    var epRef = curMilestoneNode.SelectSingleNode(Queries.VMP_GetAEPRefFromMilestone);
                    if (epRef != null)
                    {
                        var progressReferenceID = epRef.Attributes["link"].Value;

                        //Get decision gate reference taskId
                        var dgNode = modelDocument.SelectSingleNode(String.Format(Queries.MDL_GetDecisionGateReferenceId, progressReferenceID));
                        if (dgNode != null)
                        {
                            var dgReferenceId = dgNode.Attributes["link"].Value;

                            //Set taskId to decision gate taskId
                            decisionGate.Id = dgReferenceId;

                            //Get type and description of the dg
                            var typeNode = modelDocument.SelectSingleNode(String.Format(Queries.MDL_GetDecisionGateType, dgReferenceId));
                            decisionGate.ExtendedData["TypeName"] = typeNode != null ? typeNode.InnerText : string.Empty;

                            var descNode = modelDocument.SelectSingleNode(String.Format(Queries.MDL_GetDecisionGateDescription, dgReferenceId));
                            decisionGate.Description = descNode != null ? Helper.GetTextFromHtmlString(descNode.InnerXml) : string.Empty;

                            #region Determine phase and iteration

                            if (dgReferenceId.Equals("3650fb30c844c0") /*Angebot abgegeben*/||
                                dgReferenceId.Equals("56f6fb30c54fd7") /*Projekt definiert*/||
                                dgReferenceId.Equals("de21fb30c4aec0") /*Projekt definiert*/)
                            {
                                mainStatusNr = 0;
                            }
                            else
                            {
                                if (dgReferenceId.Equals("16970fb30c68166") /*Anforderungen festgelegt*/||
                                    dgReferenceId.Equals("645dfb30c8d591") /*Projekt beauftragt*/)
                                {
                                    mainStatusNr++;
                                    iterationNr = 0;
                                    inIteration = false;
                                }

                                if (dgReferenceId.Equals("6674fb30d05e5b") /*Projekt abgeschlossen*/)
                                {
                                    mainStatusNr = 99;
                                    iterationNr = 0;
                                    inIteration = false;
                                }

                                if (dgReferenceId.Equals("101d2fb30cff476") /*Iteration geplant*/)
                                {
                                    iterationNr++;
                                    inIteration = true;
                                }
                            }

                            #endregion

                            //Save phase and iteration
                            decisionGate.ExtendedData["DevPhase"] = mainStatusNr;
                            if (inIteration)
                                decisionGate.ExtendedData["DevIteration"] = iterationNr;
                        }
                    }
                    else
                    {
                        decisionGate.ExtendedData["TypeName"] = decisionGate.Name;
                        decisionGate.Description = string.Empty;
                    }

                    //Add new milestone to list
                    Milestones.Add(decisionGate);
                }
                catch (Exception e)
                {
                    log.AddEntryAndThrowException(string.Format(Language.Error_Parsing_Milestones, e.Message));
                }
            }

            #region Write milestones' predecessors and missing DevPhases

            foreach (DecisionGate curDG in Milestones)
            {
                //Predecessors
                object predecessorIDListObj;
                if (curDG.ExtendedData.TryGetValue("__VMXT_PredecessorList", out predecessorIDListObj))
                {
                    var predecessorIdList = predecessorIDListObj as List<string>;
                    if(predecessorIdList == null)
                        continue;
                    foreach (string curPredecessorID in predecessorIdList)
                    {
                        IMilestone predecessorMilestone = this.GetMilestoneByID(curPredecessorID);
                        if(predecessorMilestone == null)
                            continue;

                        if(this.GetAssociation(curDG, predecessorMilestone) != null)
                            continue; //Skip duplicates

                        var newAssoc = new MilestoneToPredecessor(curDG, predecessorMilestone);
                        Associations.Add(newAssoc);
                    }
                }
            }

            //Missing data
            var runCount = 0;
            while (runCount < 2*Milestones.Count)
            {
                runCount++;
                bool runFailed = false;

                foreach (DecisionGate curDG in Milestones)
                {
                    if (curDG.ExtendedData.ContainsKey("DevPhase"))
                        continue;

                    var predecessorAssocs = this.GetAssociationsWithSource<IMilestone, IMilestone>(curDG);
                    if (predecessorAssocs.Length > 0)
                    {
                        IMilestone curPredecessor = (predecessorAssocs[0] as Association<IMilestone, IMilestone>).Destination;
                        if (!curPredecessor.ExtendedData.ContainsKey("DevPhase"))
                        {
                            runFailed = true;
                            break;
                        }

                        object predDevPhase;
                        if(curPredecessor.ExtendedData.TryGetValue("DevPhase", out predDevPhase))
                            curDG.ExtendedData["DevPhase"] = predDevPhase as string;

                        if (curPredecessor.ExtendedData.ContainsKey("DevIteration"))
                            curDG.ExtendedData["DevIteration"] = curPredecessor.ExtendedData["DevIteration"];
                    }
                }

                if (!runFailed)
                    break;
            }

            #endregion
        }

        /// <summary>
        /// Loads an activity by its id.
        /// </summary>
        /// <param name="activityId">Activity id.</param>
        /// <param name="modelDocument">V-Model document.</param>
        /// <param name="log">Log to be used.</param>
        /// <returns>The loaded activity.</returns>
        private IActivity LoadActivity(string activityId, XmlNode modelDocument, Log log)
        {
            //Check whether this activity has already been loaded
            var existingActivity = this.GetActivityByID(activityId);
            if (existingActivity != null)
                return existingActivity;

            //Load activity
            var activityNode = modelDocument.SelectSingleNode(String.Format(Queries.MDL_GetActivityByID, activityId));
            if (activityNode != null)
            {
                var activity = new Activity(activityNode);

                Activities.Add(activity);
                return activity;
            }
            return null;
        }

        /// <summary>
        /// Loads all roles from a given V-Model document.
        /// </summary>
        /// <param name="modelDocument">V-Model document.</param>
        /// <param name="log">Log to be used.</param>
        private void LoadRoles(XmlDocument modelDocument, Log log)
        {
            var roleList = modelDocument.SelectNodes(Queries.MDL_GetRoles);
            foreach (XmlNode roleNode in roleList)
            {
                try
                {
                    Roles.Add(new Role(roleNode));
                }
                catch (Exception e)
                {
                    log.AddEntry(string.Format(Language.Error_Parsing_Role, e.Message), LogEntryType.Error);
                }
            }
        }

        /// <summary>
        /// Loads products from a VMP file.
        /// 
        /// Precondition: VBs have been loaded already.
        /// </summary>
        /// <param name="modelDocument">V-Model document.</param>
        /// <param name="vmpDocument">VMP document.</param>
        /// <param name="log">Log to be used.</param>
        private void LoadProducts(XmlDocument modelDocument, XmlDocument vmpDocument, Log log)
        {
            foreach (string curVbId in ProjectVbIdList)
            {
                var curVbProductNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetProductsByVbId, curVbId));
                foreach (XmlNode curProductNode in curVbProductNodes)
                {
                    try
                    {
                        IArtifact artifact = new Product(curProductNode);
                        Artifacts.Add(artifact);

                        //Create discipline association
                        var disciplineRefLink = curProductNode.SelectSingleNode("DisziplinRef/@link");
                        if(disciplineRefLink == null)
                            continue;
                        IDiscipline discipline = LoadDiscipline(disciplineRefLink.Value, modelDocument, log);

                        var pdAssoc = new ArtifactToDiscipline(artifact, discipline);
                        Associations.Add(pdAssoc);
                    }
                    catch (Exception e)
                    {
                        log.AddEntry(string.Format(Language.Error_Parsing_Artifact, e.Message), LogEntryType.Error);
                    }
                }
            }
        }

        /// <summary>
        /// Loads a discipline by id from a given V-Model document.
        /// </summary>
        /// <param name="disciplineId">Discipline id.</param>
        /// <param name="modelDocument">V-Model document.</param>
        /// <param name="log">Log to be used.</param>
        private IDiscipline LoadDiscipline(string disciplineId, XmlDocument modelDocument, Log log)
        {
            //Check if discipline is already loaded
            var existingDiscipline = this.GetDisciplineByID(disciplineId);
            if (existingDiscipline != null)
                return existingDiscipline;

            //Load discipline
            var disciplineNode = modelDocument.SelectSingleNode(string.Format(Queries.MDL_GetDisciplineByID, disciplineId));
            if(disciplineNode == null)
                return null;

            var discipline = new Discipline(disciplineNode);
            Disciplines.Add(discipline);

            return discipline;
        }

        /// <summary>
        /// Loads associations between existing objects from a given V-Model document.
        /// 
        /// Precondition: Products, VBs and decision gates have been loaded.
        /// </summary>
        /// <param name="modelDocument">V-Model document.</param>
        /// <param name="log">Log to be used.</param>
        private void LoadDependencies(XmlNode modelDocument, Log log)
        {
            foreach (string curVbId in ProjectVbIdList)
            {
                LoadDependenciesActivityToArtifact(curVbId, modelDocument, log);

                LoadDependenciesArtifactToMilestone(curVbId, modelDocument, log);

                LoadProductDependencies<RoleIsResponsibleForArtifact>(
                    modelDocument.SelectNodes(string.Format(Queries.MDL_GetRoleResponsibleForProductDependencies, curVbId)));
                LoadProductDependencies<RoleIsParticipatingInArtifact>(
                    modelDocument.SelectNodes(string.Format(Queries.MDL_GetRoleParticipatingInProductDependencies, curVbId)));

                LoadDependenciesArtifactContent(curVbId, modelDocument, log);
                LoadDependenciesArtifactGeneration(curVbId, modelDocument, log);
                LoadDependenciesArtifactComposition(curVbId, modelDocument, log);


                LoadDependenciesTopicToArtifact(curVbId, modelDocument, log);
            }

            LoadDependenciesTaskToActivity(modelDocument, log);
        }

        private void LoadDependenciesActivityToArtifact(string vbId, XmlNode modelDocument, Log log)
        {
            var activityToProductNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetProductActivityDependencies, vbId));
            foreach (XmlNode curAtoPNode in activityToProductNodes)
            {
                //Get artifact id of this association
                var productRefLink = curAtoPNode.SelectSingleNode("ProduktRef/@link");
                if (productRefLink == null)
                    continue;
                var artifactId = productRefLink.Value;


                var curProduct = this.GetArtifactByID(artifactId);
                if (curProduct != null)
                {
                    //Get activity id
                    var activityRefLink = curAtoPNode.SelectSingleNode("AktivitätRef/@link");
                    if (activityRefLink == null)
                        continue;
                    var newActivityId = activityRefLink.Value;

                    //Load activity
                    var activity = LoadActivity(newActivityId, modelDocument, log);

                    //Do not create already existing associations
                    if (this.GetAssociation(activity, curProduct) != null)
                        continue;

                    //Create new association
                    var newPaDep = new ActivityToArtifact(activity, curProduct);
                    SetAssociationIdAndName(newPaDep, curAtoPNode);

                    Associations.Add(newPaDep);
                }
            }
        }

        private void LoadDependenciesArtifactToMilestone(string vbId, XmlNode modelDocument, Log log)
        {
            var productToDecisionGateNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetProductDecisionGateDependencies, vbId));
            foreach (XmlNode curPtoDNode in productToDecisionGateNodes)
            {
                //Get artifact id
                var artifactRefLink = curPtoDNode.SelectSingleNode("ProduktRef/@link");
                if (artifactRefLink == null)
                    continue;
                var artifactId = artifactRefLink.Value;


                //Find product, having this id
                var curProduct = this.GetArtifactByID(artifactId);
                if (curProduct != null)
                {
                    //Get milestones
                    foreach (XmlNode decisionGateRefLink in curPtoDNode.SelectNodes("EntscheidungspunktRef/@link"))
                    {
                        //Find decision gate, having this id
                        var curDecisionGate = this.GetMilestoneByID(decisionGateRefLink.Value);
                        if (curDecisionGate != null)
                        {
                            //Do not create already existing associations
                            if (this.GetAssociation(curProduct, curDecisionGate) != null)
                                continue;

                            //Create association
                            var newProductMilestonesDependency = new ArtifactToMilestone(curProduct, curDecisionGate);
                            SetAssociationIdAndName(newProductMilestonesDependency, curPtoDNode);

                            //Add dependency
                            Associations.Add(newProductMilestonesDependency);
                        }
                    }
                }
            }
        }

        private void LoadDependenciesTopicToArtifact(string vbId, XmlNode modelDocument, Log log)
        {
            var ptDepNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetTopics, vbId));
            foreach (XmlNode curPt in ptDepNodes)
            {
                var topicRefLink = curPt.SelectSingleNode("ThemaRef/@link");
                if (topicRefLink == null)
                    continue;
                var topic = LoadTopic(topicRefLink.Value, modelDocument, log);
                if (topic == null)
                    continue;

                //Load products
                foreach (XmlNode artifactRefLink in curPt.SelectNodes("ProduktRef/@link"))
                {
                    var artifact = this.GetArtifactByID(artifactRefLink.Value);
                    if (artifact == null)
                        continue; //Only create associations to loaded products

                    //Don't create already existing associations
                    if (this.GetAssociation(topic, artifact) != null)
                        continue;

                    //Create and add dependency
                    var newDependency = new TopicToArtifact(topic, artifact);
                    SetAssociationIdAndName(newDependency, curPt);

                    Associations.Add(newDependency);
                }
            }
        }

        private void LoadDependenciesTaskToActivity(XmlNode modelDocument, Log log)
        {
            foreach (var curActivity in Activities)
            {
                var belongingTaskNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetTasksForActivity, curActivity.Id));
                foreach (XmlNode curTaskNode in belongingTaskNodes)
                {
                    ITask newTask = new Task(curTaskNode);

                    //Add task if necessary
                    var existingTask = this.GetTaskByID(newTask.Id);
                    if(existingTask != null)
                        newTask = existingTask;
                    else
                        Tasks.Add(newTask);

                    //Create and add dependency
                    var newDependency = new TaskToActivity(newTask, curActivity);
                    Associations.Add(newDependency);
                }
            }
        }

        private void LoadDependenciesArtifactContent(string vbId, XmlNode modelDocument, Log log)
        {
            var contentDepNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetProductsContentDependencies, vbId));
            foreach (XmlNode curDep in contentDepNodes)
            {
                var curDepIdAttribute = curDep.Attributes["id"];
                if(curDepIdAttribute == null)
                    continue;
                var curDepNameNode = curDep.SelectSingleNode("Name");
                if(curDepNameNode == null)
                    continue;


                //Get correct name and description text, if available
                var curDepDesc = string.Empty;
                var curDepDescRaw = string.Empty;

                var contentDependency = curDep.SelectSingleNode("InhaltlicheAbhängigkeitRef");
                if (contentDependency != null)
                {
                    var contentDependencyLink = contentDependency.Attributes["link"];
                    if(contentDependencyLink == null)
                        continue;
                    var contentDependencyNode =
                        curDep.OwnerDocument.SelectSingleNode(string.Format(Queries.MDL_GetContentDependencyByID, contentDependencyLink.Value));
                    if (contentDependencyNode != null)
                    {
                        var descNode = contentDependencyNode.SelectSingleNode("Beschreibung");
                        if(descNode != null)
                            curDepDescRaw = descNode.InnerText;
                        curDepDesc = Helper.GetTextFromHtmlString(curDepDescRaw);
                    }
                }
                //-----------------------------------------------------

                //Get dependent artifacts
                var dependentArtifacts = new List<IArtifact>();
                foreach (XmlNode dependentProductRefNode in curDep.SelectNodes("ProduktRef"))
                {
                    var curProdLink = dependentProductRefNode.Attributes["link"];
                    if(curProdLink == null)
                        continue;
                    var linkedProd = this.GetArtifactByID(curProdLink.Value);
                    if(linkedProd != null)
                        dependentArtifacts.Add(linkedProd);
                }

                //Create associations
                foreach (IArtifact curProd in dependentArtifacts)
                {
                    foreach (IArtifact curDependentProd in dependentArtifacts)
                    {
                        if (!curProd.Equals(curDependentProd))
                        {
                            var newDep = new ContentDependency(curProd, curDependentProd)
                                { Id = curDepIdAttribute.Value, Name = curDepNameNode.InnerText, Description = curDepDesc };
                            newDep.ExtendedData["DescriptionRaw"] = curDepDescRaw;
                            Associations.Add(newDep);
                        }
                    }
                }
            }
        }

        private void LoadDependenciesArtifactGeneration(string vbId, XmlNode modelDocument, Log log)
        {
            var dependentProductsNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetProductGenerationDestinationDependencies, vbId));
            foreach (XmlNode curDependentProdNode in dependentProductsNodes)
            {
                //Get dependency id   ("Erzeugende Abhaengigkeit")
                var dependencyRefLink = curDependentProdNode.SelectSingleNode("ErzeugendeAbhängigkeitRef/@link");
                if(dependencyRefLink == null)
                    continue;

                //Get dependency
                var depName = string.Empty;
                var depDescriptionRaw = string.Empty;
                var depDescription = string.Empty;

                var dependencyNode =
                    modelDocument.SelectSingleNode(string.Format(Queries.MDL_GetProductGenerationConnectorDependency, dependencyRefLink.Value));
                if (dependencyNode != null)
                {
                    var nameNode = dependencyNode.SelectSingleNode("Name");
                    if(nameNode != null)
                        depName = nameNode.InnerText;

                    var descNode = dependencyNode.SelectSingleNode("Beschreibung");
                    if(descNode != null)
                        depDescriptionRaw = descNode.InnerText;
                    depDescription = Helper.GetTextFromHtmlString(depDescriptionRaw);
                }

                //Get "Zu Produkt" nodes
                var productRefNodes = curDependentProdNode.SelectNodes("ProduktRef");
                foreach (XmlNode curToProductRefNode in productRefNodes)
                {
                    var curProductLink = curToProductRefNode.Attributes["link"];
                    if(curProductLink == null)
                        continue;

                    IArtifact sourceArtifact = this.GetArtifactByID(curProductLink.Value);
                    if(sourceArtifact == null)
                        continue;

                    var destinationDependencyNodes =
                        modelDocument.SelectNodes(string.Format(Queries.MDL_GetProductGenerationSourceDependencies, dependencyRefLink.Value));
                    foreach (XmlNode curDestinationNode in destinationDependencyNodes)
                    {
                        var destinationProductNodes = curDestinationNode.ParentNode.SelectNodes("ProduktRef");
                        foreach (XmlNode curDestinationProductNode in destinationProductNodes)
                        {
                            var curDestinationProductLinkAttribute = curDestinationProductNode.Attributes["link"];
                            if(curDestinationProductLinkAttribute == null)
                                continue;

                            IArtifact destinationArtifact = this.GetArtifactByID(curDestinationProductLinkAttribute.Value);
                            if (destinationArtifact == null)
                                continue;

                            //Create and add new dependency
                            var newDependency = new GenerationDependency(sourceArtifact, destinationArtifact)
                                                    {
                                                        Id = dependencyRefLink.Value,
                                                        Name = depName,
                                                        Description = depDescription
                                                    };
                            newDependency.ExtendedData["DescriptionRaw"] = depDescriptionRaw;

                            Associations.Add(newDependency);
                        }
                    }
                }
            }
        }

        private void LoadDependenciesArtifactComposition(string vbId, XmlNode modelDocument, Log log)
        {
            var compositionDepNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetProductCompositionDependencies, vbId));
            foreach (XmlNode curDep in compositionDepNodes)
            {
                var curDepIdAttribute = curDep.Attributes["id"];
                if (curDepIdAttribute == null)
                    continue;
                var curDepNameNode = curDep.SelectSingleNode("Name");
                if (curDepNameNode == null)
                    continue;


                //Get "parent" artifacts
                var parentArtifactNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetProductCompositionParents, curDepIdAttribute.Value));
                foreach (XmlNode curParentArtifactNode in parentArtifactNodes)
                {
                    foreach (XmlNode curParentArtifactRefLink in curParentArtifactNode.SelectNodes("ProduktRef/@link"))
                    {
                        IArtifact parentArtifact = this.GetArtifactByID(curParentArtifactRefLink.Value);
                        if(parentArtifact == null)
                            continue;

                        //Get child artifacts
                        var childArtifactNodes = modelDocument.SelectNodes(string.Format(Queries.MDL_GetProductCompositionChilds, curDepIdAttribute.Value));
                        foreach (XmlNode curChildArtifactNode in childArtifactNodes)
                        {
                            foreach (XmlNode curChildArtifactRefLink in curChildArtifactNode.SelectNodes("ProduktRef/@link"))
                            {
                                IArtifact childArtifact = this.GetArtifactByID(curChildArtifactRefLink.Value);
                                if(childArtifact == null)
                                    continue;

                                //Do not create duplicates
                                if ((this.GetAssociation<IArtifact, IArtifact>(parentArtifact, childArtifact) as CompositionDependency) != null)
                                    continue;

                                var newDep = new CompositionDependency(parentArtifact, childArtifact) { Id = curDepIdAttribute.InnerText, Name = curDepNameNode.Value };
                                Associations.Add(newDep);
                            }
                        }
                    }
                }
            }
        }

        private static void SetAssociationIdAndName(IAssociation association, XmlNode associationNode)
        {
            var assocIdAttribute = associationNode.Attributes["id"];
            if (assocIdAttribute != null)
                association.Id = assocIdAttribute.Value;

            var assocNameNode = associationNode.SelectSingleNode("Name");
            if (assocNameNode != null)
                association.Name = assocNameNode.InnerText;
        }

        private void LoadProductDependencies<T>(XmlNodeList productToRoleNodes) where T : RoleToArtifact
        {
            foreach (XmlNode curPtoR in productToRoleNodes)
            {
                var roleRefLink = curPtoR.SelectSingleNode("RolleRef/@link");
                if(roleRefLink == null)
                    continue;

                //Find role, having this id
                var curRole = this.GetRoleByID(roleRefLink.Value);
                if (curRole == null)
                    continue;

                foreach (XmlNode artifactRefLink in curPtoR.SelectNodes("ProduktRef/@link"))
                {
                    //Find product, having this id
                    var curProduct = this.GetArtifactByID(artifactRefLink.Value);
                    if (curProduct == null)
                        continue;

                    //Do not create already existing associations
                    if (this.GetAssociation(curRole, curProduct) != null)
                        continue;

                    //Create dependency
                    RoleToArtifact newPrDependency = Activator.CreateInstance(typeof (T), new object[] {curRole, curProduct}) as T;
                    SetAssociationIdAndName(newPrDependency, curPtoR);

                    Associations.Add(newPrDependency);
                }
            }
        }

        /// <summary>
        /// Loads a topic.
        /// </summary>
        /// <param name="topicId">Id of the topic to be loaded.</param>
        /// <param name="modelDocument">V-Model document.</param>
        /// <param name="log">Log to be used.</param>
        private ITopic LoadTopic(string topicId, XmlNode modelDocument, Log log)
        {
            //Check if topic exists already
            var existingTopic = this.GetTopicByID(topicId);
            if (existingTopic != null)
                return existingTopic;

            //Get topic node
            var topicNode = modelDocument.SelectSingleNode(string.Format(Queries.MDL_GetTopicByID, topicId));
            if (topicNode == null)
                return null;

            //Add new topic
            var topic = new Topic(topicNode);
            Topics.Add(topic);

            //Load subtopics
            LoadSubTopics(topicNode.SelectNodes(Queries.MDL_GetSubtopics), topic);

            return topic;
        }

        private void LoadSubTopics(XmlNodeList subtopicNodes, ITopic parentTopic)
        {
            foreach (XmlNode curSubtopicNode in subtopicNodes)
            {
                //Load topic
                ITopic curSubtopic = new Topic(curSubtopicNode);

                //Create association
                var curDependency = new TopicToSubTopic(parentTopic, curSubtopic);
                Associations.Add(curDependency);

                //Load subtopics
                LoadSubTopics(curSubtopicNode.SelectNodes(Queries.MDL_GetSubtopics), curSubtopic);
            }
        }

        #endregion

        #region IProcessProvider Members

        public List<IActivity> Activities { get; private set; }

        public List<ITask> Tasks { get; private set; }

        public List<IArtifact> Artifacts { get; private set; }

        public List<IRole> Roles { get; private set; }

        public List<ITopic> Topics { get; private set; }

        public List<IDiscipline> Disciplines { get; private set; }

        public List<IMilestone> Milestones { get; private set; }

        public List<IAssociation> Associations { get; private set; }

        public string Name { get; private set; }

        public string Description { get; private set; }

        public Dictionary<string, object> ExtendedData
        {
            get { return _ExtendedData; }
        }

        /// <summary>
        /// Path of the VModellXT.xml.
        /// </summary>
        public string VModellFile { get; set; }

        /// <summary>
        /// Path of the project file to be read. Must not be null, when UseInternalTailoring is set to false.
        /// </summary>
        public string ProjectFile { get; set; }

        /// <summary>
        /// Gets or sets the tailoring builder for the internal tailoring.
        /// </summary>
        public TailoringBuilder InternalTailoring { get; set; }

        /// <summary>
        /// Indicates whether the internal tailoring mechanism is used. If so, InternalTailoring must be valid.
        /// </summary>
        public bool UseInternalTailoring { get; set; }

        /// <summary>
        /// Indicates whether the internally tailored model and project file should be saved.
        /// </summary>
        public bool SaveInternalTailoringModel { get; set; }

        /// <summary>
        /// Path where the internally tailored model and project file should be saved.
        /// </summary>
        public string SaveInternalTailoringModelPath { get; set; }

        ///// <summary>
        ///// When a PET project file was loaded, the internalTailoring node is stored here to let the internal tailoring deserialize it.
        ///// The cause for this is that at the time of the 
        ///// </summary>
        //public XmlNode InternalTailoringDeserializationNode { get; set; }


        public string GetConfigPageName()
        {
            return "VModellConfigFrame.xaml";
        }

        public void Serialize(XmlNode outputParentNode)
        {
            var xmlDoc = outputParentNode.OwnerDocument;

            XmlNode settingsNode = xmlDoc.CreateElement("settings");
            outputParentNode.AppendChild(settingsNode);

            var modelFileAttribute = xmlDoc.CreateAttribute("modelFile");
            modelFileAttribute.Value = VModellFile;
            settingsNode.Attributes.Append(modelFileAttribute);

            var projectFileAttribute = xmlDoc.CreateAttribute("projectFile");
            projectFileAttribute.Value = ProjectFile;
            settingsNode.Attributes.Append(projectFileAttribute);


            //Internal tailoring settings
            XmlNode tailoringNode = xmlDoc.CreateElement("internalTailoring");
            settingsNode.AppendChild(tailoringNode);

            var useInternalTailoringAttribute = xmlDoc.CreateAttribute("use");
            useInternalTailoringAttribute.Value = UseInternalTailoring.ToString();
            tailoringNode.Attributes.Append(useInternalTailoringAttribute);

            var projectNameAttribute = xmlDoc.CreateAttribute("pName");
            projectNameAttribute.Value = InternalTailoring.ProjName;
            tailoringNode.Attributes.Append(projectNameAttribute);

            var projectTypeAttribute = xmlDoc.CreateAttribute("pType");
            projectTypeAttribute.Value = InternalTailoring.ProjType.Id;
            tailoringNode.Attributes.Append(projectTypeAttribute);

            var projectTypeVarAttribute = xmlDoc.CreateAttribute("pTypeVar");
            projectTypeVarAttribute.Value = InternalTailoring.ProjTypeVariant.Id;
            tailoringNode.Attributes.Append(projectTypeVarAttribute);

            var saveModelAttribute = xmlDoc.CreateAttribute("saveModel");
            saveModelAttribute.Value = SaveInternalTailoringModel.ToString();
            tailoringNode.Attributes.Append(saveModelAttribute);

            var saveModelPathAttribute = xmlDoc.CreateAttribute("saveModelPath");
            saveModelPathAttribute.Value = SaveInternalTailoringModelPath;
            tailoringNode.Attributes.Append(saveModelPathAttribute);

            //Internal tailoring characteristics
            foreach (var projChar in InternalTailoring.ProjCharacteristics)
            {
                XmlNode charNode = xmlDoc.CreateElement("pChar");
                tailoringNode.AppendChild(charNode);

                var charIdAttribute = xmlDoc.CreateAttribute("id");
                charIdAttribute.Value = projChar.Key.Id;
                charNode.Attributes.Append(charIdAttribute);

                var charValAttribute = xmlDoc.CreateAttribute("value");
                charValAttribute.Value = projChar.Value.Id;
                charNode.Attributes.Append(charValAttribute);
            }
        }

        public void Deserialize(XmlNode inputParentNode)
        {
            var settingsNode = inputParentNode.SelectSingleNode("settings");

            VModellFile = null;
            XmlNode modelFileNode = settingsNode.Attributes["modelFile"];
            if(modelFileNode != null)
            {
                VModellFile = modelFileNode.Value;
                if (!File.Exists(VModellFile))
                    VModellFile = null;
            }

            ProjectFile = null;
            XmlNode projectFileNode = settingsNode.Attributes["projectFile"];
            if (projectFileNode != null)
            {
                ProjectFile = projectFileNode.Value;
                if (!File.Exists(ProjectFile))
                    ProjectFile = null;
            }

            //Internal tailoring settings
            XmlNode tailoringNode = settingsNode.SelectSingleNode("internalTailoring");
            if (tailoringNode != null)
            {
                XmlNode useInternalTailoringNode = tailoringNode.Attributes["use"];
                if (useInternalTailoringNode != null)
                {
                    bool internalTailoring;
                    bool.TryParse(useInternalTailoringNode.Value, out internalTailoring);
                    UseInternalTailoring = internalTailoring;
                }

                //Create internal tailoring structure
                if (VModellFile != null)
                {
                    InternalTailoring = new TailoringBuilder(new Tailoring.VModellXT(Path.GetDirectoryName(VModellFile)));

                    XmlNode projectNameAttribute = tailoringNode.Attributes["pName"];
                    if (projectNameAttribute != null)
                        InternalTailoring.ProjName = projectNameAttribute.Value;

                    //Deserialize project type
                    XmlNode projectTypeAttribute = tailoringNode.Attributes["pType"];
                    if (projectTypeAttribute != null)
                    {
                        var projectTypeId = projectTypeAttribute.Value;
                        foreach (var pt in InternalTailoring.Model.ProjectTypes)
                        {
                            if (pt.Id.Equals(projectTypeId))
                            {
                                InternalTailoring.ProjType = pt;
                                break;
                            }
                        }
                        if (InternalTailoring.ProjType != null)
                        {
                            //Deserialize project type variant
                            XmlNode projectTypeVarAttribute = tailoringNode.Attributes["pTypeVar"];
                            if (projectTypeVarAttribute != null)
                            {
                                var projectTypeVarId = projectTypeVarAttribute.Value;
                                foreach (var ptv in InternalTailoring.ProjType.ProjectTypeVariants)
                                {
                                    if (ptv.Id.Equals(projectTypeVarId))
                                    {
                                        InternalTailoring.ProjTypeVariant = ptv;
                                        break;
                                    }
                                }
                                if (InternalTailoring.ProjTypeVariant != null)
                                {
                                    //Deserialize project characteristics
                                    var allCharacteristics = InternalTailoring.ProjType.ProjectCharacteristics.Union(InternalTailoring.ProjTypeVariant.ProjectCharacteristics);
                                    
                                    XmlNodeList projectCharNodes = tailoringNode.SelectNodes("pChar");
                                    foreach (XmlNode charNode in projectCharNodes)
                                    {
                                        XmlNode charIdAttribute = charNode.Attributes["id"];
                                        XmlNode charValIdAttribute = charNode.Attributes["value"];
                                        if (charIdAttribute != null && charValIdAttribute != null)
                                        {
                                            var charId = charIdAttribute.Value;
                                            var charValId = charValIdAttribute.Value;

                                            foreach (var pc in allCharacteristics)
                                            {
                                                if (pc.Id.Equals(charId))
                                                {
                                                    //Characteristic found. Search value
                                                    foreach (var pcv in pc.Values)
                                                    {
                                                        if (pcv.Id.Equals(charValId))
                                                        {
                                                            InternalTailoring.ProjCharacteristics.Add(pc, pcv);
                                                            pc.DefaultValue = pcv; //For tailoring ui
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                XmlNode saveModelAttribute = tailoringNode.Attributes["saveModel"];
                if (saveModelAttribute != null)
                {
                    bool saveModel;
                    bool.TryParse(saveModelAttribute.Value, out saveModel);
                    SaveInternalTailoringModel = saveModel;
                }

                XmlNode saveModelPathAttribute = tailoringNode.Attributes["saveModelPath"];
                if (saveModelPathAttribute != null)
                {
                    SaveInternalTailoringModelPath = saveModelPathAttribute.Value;
                }
            }
        }

        #endregion
    }
}